home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / lib / htmlLib.js
Text File  |  2010-01-15  |  25KB  |  746 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. const Ci = Components.interfaces;
  6. const SHOW_ALL = Ci.nsIDOMNodeFilter.SHOW_ALL;
  7.  
  8. /**
  9.  * @class Static utility class. Contains utilities used for displaying and
  10.  *        searching a HTML tree.
  11.  */
  12. Firebug.HTMLLib =
  13. {
  14.     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  15.     // Node Search Utilities
  16.     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  17.     /**
  18.      * Constructs a NodeSearch instance.
  19.      *
  20.      * @class Class used to search a DOM tree for the given text. Will display
  21.      *        the search results in a IO Box.
  22.      *
  23.      * @constructor
  24.      * @param {String} text Text to search for
  25.      * @param {Object} root Root of search. This may be an element or a document
  26.      * @param {Object} panelNode Panel node containing the IO Box representing the DOM tree.
  27.      * @param {Object} ioBox IO Box to display the search results in
  28.      * @param {Object} walker Optional walker parameter.
  29.      */
  30.     NodeSearch: function(text, root, panelNode, ioBox, walker)
  31.     {
  32.         root = root.documentElement || root;
  33.         walker = walker || new Firebug.HTMLLib.DOMWalker(root);
  34.         var re = new ReversibleRegExp(text, "m");
  35.         var matchCount = 0;
  36.  
  37.         /**
  38.          * Finds the first match within the document.
  39.          *
  40.          * @param {boolean} revert true to search backward, false to search forward
  41.          * @param {boolean} caseSensitive true to match exact case, false to ignore case
  42.          * @return true if no more matches were found, but matches were found previously.
  43.          */
  44.         this.find = function(reverse, caseSensitive)
  45.         {
  46.             var match = this.findNextMatch(reverse, caseSensitive);
  47.             if (match)
  48.             {
  49.                 this.lastMatch = match;
  50.                 ++matchCount;
  51.  
  52.                 var node = match.node;
  53.                 var nodeBox = this.openToNode(node, match.isValue);
  54.  
  55.                 this.selectMatched(nodeBox, node, match, reverse);
  56.             }
  57.             else if (matchCount)
  58.                 return true;
  59.             else
  60.             {
  61.                 this.noMatch = true;
  62.                 dispatch([Firebug.A11yModel], 'onHTMLSearchNoMatchFound', [panelNode.ownerPanel, text]);
  63.             }
  64.         };
  65.  
  66.         /**
  67.          * Resets the search to the beginning of the document.
  68.          */
  69.         this.reset = function()
  70.         {
  71.             delete this.lastMatch;
  72.         };
  73.  
  74.         /**
  75.          * Finds the next match in the document.
  76.          *
  77.          * The return value is an object with the fields
  78.          * - node: Node that contains the match
  79.          * - isValue: true if the match is a match due to the value of the node, false if it is due to the name
  80.          * - match: Regular expression result from the match
  81.          *
  82.          * @param {boolean} revert true to search backward, false to search forward
  83.          * @param {boolean} caseSensitive true to match exact case, false to ignore case
  84.          * @return Match object if found
  85.          */
  86.         this.findNextMatch = function(reverse, caseSensitive)
  87.         {
  88.             var innerMatch = this.findNextInnerMatch(reverse, caseSensitive);
  89.             if (innerMatch)
  90.                 return innerMatch;
  91.             else
  92.                 this.reset();
  93.  
  94.             function walkNode() { return reverse ? walker.previousNode() : walker.nextNode(); }
  95.  
  96.             var node;
  97.             while (node = walkNode())
  98.             {
  99.                 if (node.nodeType == Node.TEXT_NODE)
  100.                 {
  101.                     if (Firebug.HTMLLib.isSourceElement(node.parentNode))
  102.                         continue;
  103.                 }
  104.  
  105.                 var m = this.checkNode(node, reverse, caseSensitive);
  106.                 if (m)
  107.                     return m;
  108.             }
  109.         };
  110.  
  111.         /**
  112.          * Helper util used to scan the current search result for more results
  113.          * in the same object.
  114.          *
  115.          * @private
  116.          */
  117.         this.findNextInnerMatch = function(reverse, caseSensitive)
  118.         {
  119.             if (this.lastMatch)
  120.             {
  121.                 var lastMatchNode = this.lastMatch.node;
  122.                 var lastReMatch = this.lastMatch.match;
  123.                 var m = re.exec(lastReMatch.input, reverse, lastReMatch.caseSensitive, lastReMatch);
  124.                 if (m)
  125.                 {
  126.                     return {
  127.                         node: lastMatchNode,
  128.                         isValue: this.lastMatch.isValue,
  129.                         match: m
  130.                     };
  131.                 }
  132.  
  133.                 // May need to check the pair for attributes
  134.                 if (lastMatchNode.nodeType == Node.ATTRIBUTE_NODE
  135.                         && this.lastMatch.isValue == !!reverse)
  136.                 {
  137.                     return this.checkNode(lastMatchNode, reverse, caseSensitive, 1);
  138.                 }
  139.             }
  140.         };
  141.  
  142.         /**
  143.          * Checks a given node for a search match.
  144.          *
  145.          * @private
  146.          */
  147.         this.checkNode = function(node, reverse, caseSensitive, firstStep)
  148.         {
  149.             var checkOrder;
  150.             if (node.nodeType != Node.TEXT_NODE)
  151.             {
  152.                 var nameCheck = { name: "nodeName", isValue: false, caseSensitive: false };
  153.                 var valueCheck = { name: "nodeValue", isValue: true, caseSensitive: caseSensitive };
  154.                 checkOrder = reverse ? [ valueCheck, nameCheck ] : [ nameCheck, valueCheck ];
  155.             }
  156.             else
  157.             {
  158.                 checkOrder = [{name: "nodeValue", isValue: false, caseSensitive: caseSensitive }];
  159.             }
  160.  
  161.             for (var i = firstStep || 0; i < checkOrder.length; i++) {
  162.                 var m = re.exec(node[checkOrder[i].name], reverse, checkOrder[i].caseSensitive);
  163.                 if (m)
  164.                     return {
  165.                         node: node,
  166.                         isValue: checkOrder[i].isValue,
  167.                         match: m
  168.                     };
  169.             }
  170.         };
  171.  
  172.         /**
  173.          * Opens the given node in the associated IO Box.
  174.          *
  175.          * @private
  176.          */
  177.         this.openToNode = function(node, isValue)
  178.         {
  179.             if (node.nodeType == Node.ELEMENT_NODE)
  180.             {
  181.                 var nodeBox = ioBox.openToObject(node);
  182.                 return nodeBox.getElementsByClassName("nodeTag")[0];
  183.             }
  184.             else if (node.nodeType == Node.ATTRIBUTE_NODE)
  185.             {
  186.                 var nodeBox = ioBox.openToObject(node.ownerElement);
  187.                 if (nodeBox)
  188.                 {
  189.                     var attrNodeBox = Firebug.HTMLLib.findNodeAttrBox(nodeBox, node.nodeName);
  190.                     if (isValue)
  191.                         return getChildByClass(attrNodeBox, "nodeValue");
  192.                     else
  193.                         return getChildByClass(attrNodeBox, "nodeName");
  194.                 }
  195.             }
  196.             else if (node.nodeType == Node.TEXT_NODE)
  197.             {
  198.                 var nodeBox = ioBox.openToObject(node);
  199.                 if (nodeBox)
  200.                     return nodeBox;
  201.                 else
  202.                 {
  203.                     var nodeBox = ioBox.openToObject(node.parentNode);
  204.                     if (hasClass(nodeBox, "textNodeBox"))
  205.                         nodeBox = Firebug.HTMLLib.getTextElementTextBox(nodeBox);
  206.                     return nodeBox;
  207.                 }
  208.             }
  209.         };
  210.  
  211.         /**
  212.          * Selects the search results.
  213.          *
  214.          * @private
  215.          */
  216.         this.selectMatched = function(nodeBox, node, match, reverse)
  217.         {
  218.             setTimeout(bindFixed(function()
  219.             {
  220.                 var reMatch = match.match;
  221.                 this.selectNodeText(nodeBox, node, reMatch[0], reMatch.index, reverse, reMatch.caseSensitive);
  222.                 dispatch([Firebug.A11yModel], 'onHTMLSearchMatchFound', [panelNode.ownerPanel, match]);
  223.             }, this));
  224.         };
  225.  
  226.         /**
  227.          * Select text node search results.
  228.          *
  229.          * @private
  230.          */
  231.         this.selectNodeText = function(nodeBox, node, text, index, reverse, caseSensitive)
  232.         {
  233.             var row;
  234.  
  235.             // If we are still inside the same node as the last search, advance the range
  236.             // to the next substring within that node
  237.             if (nodeBox == this.lastNodeBox)
  238.             {
  239.                 row = this.textSearch.findNext(false, true, reverse, caseSensitive);
  240.             }
  241.  
  242.             if (!row)
  243.             {
  244.                 // Search for the first instance of the string inside the node
  245.                 function findRow(node) { return node.nodeType == Node.ELEMENT_NODE ? node : node.parentNode; }
  246.                 this.textSearch = new TextSearch(nodeBox, findRow);
  247.                 row = this.textSearch.find(text, reverse, caseSensitive);
  248.                 this.lastNodeBox = nodeBox;
  249.             }
  250.  
  251.             if (row)
  252.             {
  253.                 var sel = panelNode.ownerDocument.defaultView.getSelection();
  254.                 sel.removeAllRanges();
  255.                 sel.addRange(this.textSearch.range);
  256.  
  257.                 scrollIntoCenterView(row, panelNode);
  258.                 return true;
  259.             }
  260.         };
  261.     },
  262.  
  263.     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  264.  
  265.     /**  XXXjjb this code is no longer called and won't be in 1.5; if FireFinder works out we can delete this.
  266.      * Constructs a SelectorSearch instance.
  267.      *
  268.      * @class Class used to search a DOM tree for elements matching the given
  269.      *        CSS selector.
  270.      *
  271.      * @constructor
  272.      * @param {String} text CSS selector to search for
  273.      * @param {Document} doc Document to search
  274.      * @param {Object} panelNode Panel node containing the IO Box representing the DOM tree.
  275.      * @param {Object} ioBox IO Box to display the search results in
  276.      */
  277.     SelectorSearch: function(text, doc, panelNode, ioBox)
  278.     {
  279.         this.parent = new Firebug.HTMLLib.NodeSearch(text, doc, panelNode, ioBox);
  280.  
  281.         /**
  282.          * Finds the first match within the document.
  283.          *
  284.          * @param {boolean} revert true to search backward, false to search forward
  285.          * @param {boolean} caseSensitive true to match exact case, false to ignore case
  286.          * @return true if no more matches were found, but matches were found previously.
  287.          */
  288.         this.find = this.parent.find;
  289.  
  290.         /**
  291.          * Resets the search to the beginning of the document.
  292.          */
  293.         this.reset = this.parent.reset;
  294.  
  295.         /**
  296.          * Opens the given node in the associated IO Box.
  297.          *
  298.          * @private
  299.          */
  300.         this.openToNode = this.parent.openToNode;
  301.  
  302.         try
  303.         {
  304.             // http://dev.w3.org/2006/webapi/selectors-api/
  305.             this.matchingNodes = doc.querySelectorAll(text);
  306.             this.matchIndex = 0;
  307.         }
  308.         catch(exc)
  309.         {
  310.             FBTrace.sysout("SelectorSearch FAILS "+exc, exc);
  311.         }
  312.  
  313.         /**
  314.          * Finds the next match in the document.
  315.          *
  316.          * The return value is an object with the fields
  317.          * - node: Node that contains the match
  318.          * - isValue: true if the match is a match due to the value of the node, false if it is due to the name
  319.          * - match: Regular expression result from the match
  320.          *
  321.          * @param {boolean} revert true to search backward, false to search forward
  322.          * @param {boolean} caseSensitive true to match exact case, false to ignore case
  323.          * @return Match object if found
  324.          */
  325.         this.findNextMatch = function(reverse, caseSensitive)
  326.         {
  327.             if (!this.matchingNodes || !this.matchingNodes.length)
  328.                 return undefined;
  329.  
  330.             if (reverse)
  331.             {
  332.                 if (this.matchIndex > 0)
  333.                     return { node: this.matchingNodes[this.matchIndex--], isValue: false, match: "?XX?"};
  334.                 else
  335.                     return undefined;
  336.             }
  337.             else
  338.             {
  339.                 if (this.matchIndex < this.matchingNodes.length)
  340.                     return { node: this.matchingNodes[this.matchIndex++], isValue: false, match: "?XX?"};
  341.                 else
  342.                     return undefined;
  343.             }
  344.         };
  345.  
  346.         /**
  347.          * Selects the search results.
  348.          *
  349.          * @private
  350.          */
  351.         this.selectMatched = function(nodeBox, node, match, reverse)
  352.         {
  353.             setTimeout(bindFixed(function()
  354.             {
  355.                 ioBox.select(node, true, true);
  356.                 dispatch([Firebug.A11yModel], 'onHTMLSearchMatchFound', [panelNode.ownerPanel, match]);
  357.             }, this));
  358.         };
  359.     },
  360.  
  361.  
  362.     /**
  363.      * Constructs a DOMWalker instance.
  364.      *
  365.      * @constructor
  366.      * @class Implements an ordered traveral of the document, including attributes and
  367.      *        iframe contents within the results.
  368.      *
  369.      *        Note that the order for attributes is not defined. This will follow the
  370.      *        same order as the Element.attributes accessor.
  371.      * @param {Element} root Element to traverse
  372.      */
  373.     DOMWalker: function(root)
  374.     {
  375.         var walker;
  376.         var currentNode, attrIndex;
  377.         var pastStart, pastEnd;
  378.         var doc = root.ownerDocument;
  379.  
  380.         function createWalker(docElement) {
  381.             var walk = doc.createTreeWalker(docElement, SHOW_ALL, null, true);
  382.             walker.unshift(walk);
  383.         }
  384.         function getLastAncestor() {
  385.             while (walker[0].lastChild()) {}
  386.             return walker[0].currentNode;
  387.         }
  388.  
  389.         /**
  390.          * Move to the previous node.
  391.          *
  392.          * @return The previous node if one exists, undefined otherwise.
  393.          */
  394.         this.previousNode = function() {
  395.             if (pastStart) {
  396.                 return undefined;
  397.             }
  398.  
  399.             if (attrIndex) {
  400.                 attrIndex--;
  401.             } else {
  402.                 var prevNode;
  403.                 if (currentNode == walker[0].root) {
  404.                     if (walker.length > 1) {
  405.                         walker.shift();
  406.                         prevNode = walker[0].currentNode;
  407.                     } else {
  408.                         prevNode = undefined;
  409.                     }
  410.                 } else {
  411.                     if (!currentNode) {
  412.                         prevNode = getLastAncestor();
  413.                     } else {
  414.                         prevNode = walker[0].previousNode();
  415.                     }
  416.                     if (!prevNode) {    // Really shouldn't occur, but to be safe
  417.                         prevNode = walker[0].root;
  418.                     }
  419.                     while ((prevNode.nodeName || "").toUpperCase() == "IFRAME") {
  420.                         createWalker(prevNode.contentDocument.documentElement);
  421.                         prevNode = getLastAncestor();
  422.                     }
  423.                 }
  424.                 currentNode = prevNode;
  425.                 attrIndex = ((prevNode || {}).attributes || []).length;
  426.             }
  427.  
  428.             if (!currentNode) {
  429.                 pastStart = true;
  430.             } else {
  431.                 pastEnd = false;
  432.             }
  433.  
  434.             return this.currentNode();
  435.         };
  436.  
  437.         /**
  438.          * Move to the next node.
  439.          *
  440.          * @return The next node if one exists, otherwise undefined.
  441.          */
  442.         this.nextNode = function() {
  443.             if (pastEnd) {
  444.                 return undefined;
  445.             }
  446.  
  447.             if (!currentNode) {
  448.                 // We are working with a new tree walker
  449.                 currentNode = walker[0].root;
  450.                 attrIndex = 0;
  451.             } else {
  452.                 // First check attributes
  453.                 var attrs = currentNode.attributes || [];
  454.                 if (attrIndex < attrs.length) {
  455.                     attrIndex++;
  456.                 } else if ((currentNode.nodeName || "").toUpperCase() == "IFRAME") {
  457.                     // Attributes have completed, check for iframe contents
  458.                     createWalker(currentNode.contentDocument.documentElement);
  459.                     currentNode = walker[0].root;
  460.                     attrIndex = 0;
  461.                 } else {
  462.                     // Next node
  463.                     var nextNode = walker[0].nextNode();
  464.                     while (!nextNode && walker.length > 1) {
  465.                         walker.shift();
  466.                         nextNode = walker[0].nextNode();
  467.                     }
  468.                     currentNode = nextNode;
  469.                     attrIndex = 0;
  470.                 }
  471.             }
  472.  
  473.             if (!currentNode) {
  474.                 pastEnd = true;
  475.             } else {
  476.                 pastStart = false;
  477.             }
  478.  
  479.             return this.currentNode();
  480.         };
  481.  
  482.         /**
  483.          * Retrieves the current node.
  484.          *
  485.          * @return The current node, if not past the beginning or end of the iteration.
  486.          */
  487.         this.currentNode = function() {
  488.             if (!attrIndex) {
  489.                 return currentNode;
  490.             } else {
  491.                 return currentNode.attributes[attrIndex-1];
  492.             }
  493.         };
  494.  
  495.         /**
  496.          * Resets the walker position back to the initial position.
  497.          */
  498.         this.reset = function() {
  499.             pastStart = false;
  500.             pastEnd = false;
  501.             walker = [];
  502.             currentNode = undefined;
  503.             attrIndex = 0;
  504.  
  505.             createWalker(root);
  506.         };
  507.  
  508.         this.reset();
  509.     },
  510.  
  511.     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  512.     // Node/Element Utilities
  513.     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  514.  
  515.     /**
  516.      * Determines if the given element is the source for a non-DOM resource such
  517.      * as Javascript source or CSS definition.
  518.      *
  519.      * @param {Element} element Element to test
  520.      * @return true if the element is a source element
  521.      */
  522.     isSourceElement: function(element)
  523.     {
  524.         var tag = element.localName.toLowerCase();
  525.         return tag == "script" || tag == "link" || tag == "style"
  526.             || (tag == "link" && element.getAttribute("rel") == "stylesheet");
  527.     },
  528.  
  529.     /**
  530.      * Retrieves the source URL for any external resource associated with a node.
  531.      *
  532.      * @param {Element} element Element to examine
  533.      * @return URL of the external resouce.
  534.      */
  535.     getSourceHref: function(element)
  536.     {
  537.         var tag = element.localName.toLowerCase();
  538.         if (tag == "script" && element.src)
  539.             return element.src;
  540.         else if (tag == "link")
  541.             return element.href;
  542.         else
  543.             return null;
  544.     },
  545.  
  546.     /**
  547.      * Retrieves the source text for inline script and style elements.
  548.      *
  549.      * @param {Element} element Script or style element
  550.      * @return Source text
  551.      */
  552.     getSourceText: function(element)
  553.     {
  554.         var tag = element.localName.toLowerCase();
  555.         if (tag == "script" && !element.src)
  556.             return element.textContent;
  557.         else if (tag == "style")
  558.             return element.textContent;
  559.         else
  560.             return null;
  561.     },
  562.  
  563.     /**
  564.      * Determines if the given element is a container element.
  565.      *
  566.      * @param {Element} element Element to test
  567.      * @return True if the element is a container element.
  568.      */
  569.     isContainerElement: function(element)
  570.     {
  571.         var tag = element.localName.toLowerCase();
  572.         switch (tag)
  573.         {
  574.             case "script":
  575.             case "style":
  576.             case "iframe":
  577.             case "frame":
  578.             case "tabbrowser":
  579.             case "browser":
  580.                 return true;
  581.             case "link":
  582.                 return element.getAttribute("rel") == "stylesheet";
  583.             case "embed":
  584.                 return element.getSVGDocument();
  585.         }
  586.         return false;
  587.     },
  588.  
  589.     /**
  590.      * Determines if the given node has any children which are elements.
  591.      *
  592.      * @param {Element} element Element to test.
  593.      * @return true if immediate children of type Element exist, false otherwise
  594.      */
  595.     hasNoElementChildren: function(element)
  596.     {
  597.         if (element.childElementCount != 0)  // FF 3.5+
  598.             return false;
  599.  
  600.         // https://developer.mozilla.org/en/XBL/XBL_1.0_Reference/DOM_Interfaces
  601.         if (element.ownerDocument instanceof Ci.nsIDOMDocumentXBL)
  602.         {
  603.             var anonChildren = element.ownerDocument.getAnonymousNodes(element);
  604.             if (anonChildren)
  605.             {
  606.                 for (var i = 0; i < anonChildren.length; i++)
  607.                 {
  608.                     if (anonChildren[i].nodeType == Node.ELEMENT_NODE)
  609.                         return false;
  610.                 }
  611.             }
  612.         }
  613.         return true;
  614.     },
  615.     
  616.     
  617.     /**
  618.      * Determines if the given node has any children which are comments.
  619.      *
  620.      * @param {Element} element Element to test.
  621.      * @return true if immediate children of type Comment exist, false otherwise
  622.      */
  623.     hasCommentChildren: function(element)
  624.     {
  625.         if (element.hasChildNodes())
  626.         {
  627.             var children = element.childNodes;
  628.             for (var i = 0; i < children.length; i++) 
  629.             {
  630.               if (children[i] instanceof Comment)
  631.                  return true;
  632.             }
  633.         };
  634.         return false;
  635.     },
  636.  
  637.  
  638.     /**
  639.      * Determines if the given node consists solely of whitespace text.
  640.      *
  641.      * @param {Node} node Node to test.
  642.      * @return true if the node is a whitespace text node
  643.      */
  644.     isWhitespaceText: function(node)
  645.     {
  646.         if (node instanceof HTMLAppletElement)
  647.             return false;
  648.         return node.nodeType == Node.TEXT_NODE && isWhitespace(node.nodeValue);
  649.     },
  650.  
  651.     /**
  652.      * Determines if a given element is empty. When the
  653.      * {@link Firebug#showTextNodesWithWhitespace} parameter is true, an element is
  654.      * considered empty if it has no child elements and is self closing. When
  655.      * false, an element is considered empty if the only children are whitespace
  656.      * nodes.
  657.      *
  658.      * @param {Element} element Element to test
  659.      * @return true if the element is empty, false otherwise
  660.      */
  661.     isEmptyElement: function(element)
  662.     {
  663.         // XXXjjb the commented code causes issues 48, 240, and 244. I think the lines should be deleted.
  664.         // If the DOM has whitespace children, then the element is not empty even if
  665.         // we decide not to show the whitespace in the UI.
  666.  
  667.         // XXXsroussey reverted above but added a check for self closing tags
  668.         if (Firebug.showTextNodesWithWhitespace)
  669.         {
  670.             return !element.firstChild && isSelfClosing(element);
  671.         }
  672.         else
  673.         {
  674.             for (var child = element.firstChild; child; child = child.nextSibling)
  675.             {
  676.                 if (!Firebug.HTMLLib.isWhitespaceText(child))
  677.                     return false;
  678.             }
  679.         }
  680.         return isSelfClosing(element);
  681.     },
  682.  
  683.     /**
  684.      * Finds the next sibling of the given node. If the
  685.      * {@link Firebug#showTextNodesWithWhitespace} parameter is set to true, the next
  686.      * sibling may be a whitespace, otherwise the next is the first adjacent
  687.      * non-whitespace node.
  688.      *
  689.      * @param {Node} node Node to analyze.
  690.      * @return Next sibling node, if one exists
  691.      */
  692.     findNextSibling: function(node)
  693.     {
  694.         if (Firebug.showTextNodesWithWhitespace)
  695.             return node.nextSibling;
  696.         else
  697.         {
  698.             // only return a non-whitespace node
  699.             for (var child = node.nextSibling; child; child = child.nextSibling)
  700.             {
  701.                 if (!Firebug.HTMLLib.isWhitespaceText(child))
  702.                     return child;
  703.             }
  704.         }
  705.     },
  706.  
  707.     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  708.     // Domplate Utilities
  709.     //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  710.  
  711.     /**
  712.      * Locates the attribute domplate node for a given element domplate. This method will
  713.      * only examine notes marked with the "nodeAttr" class that are the direct
  714.      * children of the given element.
  715.      *
  716.      * @param {Object} objectNodeBox The domplate element to look up the attribute for.
  717.      * @param {String} attrName Attribute name
  718.      * @return Attribute's domplate node
  719.      */
  720.     findNodeAttrBox: function(objectNodeBox, attrName)
  721.     {
  722.         var child = objectNodeBox.firstChild.lastChild.firstChild;
  723.         for (; child; child = child.nextSibling)
  724.         {
  725.             if (hasClass(child, "nodeAttr") && child.childNodes[1].firstChild
  726.                 && child.childNodes[1].firstChild.nodeValue == attrName)
  727.             {
  728.                 return child;
  729.             }
  730.         }
  731.     },
  732.  
  733.     /**
  734.      * Locates the text domplate node for a given text element domplate.
  735.      * @param {Object} nodeBox Text element domplate
  736.      * @return Element's domplate text node
  737.      */
  738.     getTextElementTextBox: function(nodeBox)
  739.     {
  740.         var nodeLabelBox = nodeBox.firstChild.lastChild;
  741.         return getChildByClass(nodeLabelBox, "nodeText");
  742.     }
  743. };
  744.  
  745. }});
  746.